package com.nlp.solve;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.nlp.model.Article;
import org.apache.commons.io.input.BOMInputStream;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class Main {
    private static Logger logger = LoggerFactory.getLogger(Main.class);
    private static HashSet<String> setSplitWithSemicolon;
    private static Constructor<Article> constructor;
    public static int count = 0;


    static {
        try {
            constructor = Article.class.getConstructor();
            setSplitWithSemicolon = new HashSet<>(15);
            setSplitWithSemicolon.add("FU");
            setSplitWithSemicolon.add("OI");
            setSplitWithSemicolon.add("RP");
            setSplitWithSemicolon.add("DE");
            setSplitWithSemicolon.add("RI");
            setSplitWithSemicolon.add("SC");
            setSplitWithSemicolon.add("ID");
            setSplitWithSemicolon.add("EM");
            setSplitWithSemicolon.add("BN");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }


    private static final String dir = "C:\\Users\\Administrator\\Desktop\\Cooperation between ASEAN and China\\";
    private static String outputPath = "D:\\Cache\\IdeaProjects\\nlp2.0\\src\\test\\xml\\";

    public static void main(String[] args) throws NoSuchMethodException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        //先来一次全部的
        preResolve(new File(dir), outputPath);
    }

    //读取某一文件夹下所有文件并输出至特定json文件中
    public static void preResolve(File file, String outputPath) throws IOException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                preResolve(f, outputPath);
            } else {
                resolve(f.getAbsolutePath(), outputPath + f.getParentFile().getName() + "//" + f.getName() + ".xml");
            }
        }
    }


    public static String read(String abs) throws IOException {
        StringBuilder res = new StringBuilder();
        try (DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(abs)))) {
            int b = -1;
            while ((b = dataInputStream.read()) != -1) {
                res.append((char) b);
            }
        }
        return res.toString();
    }

    public static void resolve(String file, String outputPath) throws IOException, IllegalArgumentException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        logger.info("Resolving: " + file);
        List<Article> articles = new LinkedList<>();
        try (DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new BOMInputStream(new FileInputStream(file))))) {
            int val = -1;
            StringBuilder stringBuilder = new StringBuilder();
            while ((val = dataInputStream.read()) != -1) {
                stringBuilder.append((char) val);
                if (val == '\n') {
                    if (isAnArticle(stringBuilder.toString())) {
//                        System.out.println(count);
                        Article article = solveEachArticle(stringBuilder.toString());
//                        String[] c1 = article.getC1();//获取所有作者地址，并筛选出包含广东的作者 TODO
//                        boolean in = false;
//                        for (int i = 0; c1 != null && i < c1.length; i++) {
//                            if (c1[i].contains("Guangzhou") || c1[i].contains("Guangdong")) {
//                                in = true;
//                                break;
//                            }
//                        }
//                        if (in) {
                        if (!checkTimeIn(article)) {
                            articles.add(article);
                        }
                        ;
//                        }
                        stringBuilder.delete(0, stringBuilder.length());
//                        Thread.sleep(1000);
                    }
                }
            }

            check(outputPath);
//            writeXml(articles, outputPath);
            try (DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outputPath)))) {
                JSONObject.writeJSONString(dataOutputStream, StandardCharsets.UTF_8, articles);
            }
            logger.info("articles count: {}", count);
        }
    }

    private static boolean checkTimeIn(Article article) {
        if (article.getPY() == null || article.getPY().equals("2021") || article.getPY().equals("1999")) {
            return true;
        }
        return false;
    }

    private static void writeXml(List<Article> articles, String outputPath) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, IOException {
        Document document = DocumentHelper.createDocument();
        Element element = document.addElement("Newell");
        for (int i = 0; i < articles.size(); i++) {
            writeXml(element, articles.get(i), i);
        }
        OutputFormat outputFormat = new OutputFormat();
        outputFormat.setIndent(true);
        outputFormat.setNewlines(true);
        try (BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputPath)))) {
            document.write(bufferedWriter);
        }
//        XMLWriter xmlWriter = new XMLWriter(new BufferedWriter(new FileWriter(outputPath)), outputFormat);
//        xmlWriter.write(document);
    }

    private static void writeXml(Element root, Object o, int index) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> clazz = o.getClass();
        String clazzName = "Bibliography";
        Element e = root.addElement(clazzName);
        e.addAttribute("id", String.valueOf(index));
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            String param = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method method = clazz.getMethod("get" + param);
            Object invoke = method.invoke(o);
            if (invoke == null) {
                continue;
            }
            String res = "";
            if (invoke.getClass() == String[].class) {
//                res = listToString((String[]) invoke);
            } else {
                res = invoke.toString();
            }
            if ("".equals(res)) {
                continue;
            }
            Element fieldElement = e.addElement(fieldName);
            fieldElement.setText(res);

        }
    }

    private static String listToString(String[] ss) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : ss) {
            stringBuilder.append(s).append("||");
        }
        return stringBuilder.toString();
    }


    private static void check(String outputPath) {
        File f = new File(outputPath);
        File parent = f.getParentFile();
        if (!parent.exists()) {
            check(parent.getAbsolutePath());
            parent.mkdir();
        }
    }

    private static Article solveEachArticle(String article) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, InstantiationException {
        count++;
        Article a = constructor.newInstance();
        //现在已经读出每一篇文章了，要读出每一个字段
        //用于存放每一行属性
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < article.length() - 1; i++) {
            stringBuilder.append(article.charAt(i));
            if (article.charAt(i) == '\n' && article.charAt(i + 1) != ' ') {
                String s = stringBuilder.toString();
                String fieldName = s.substring(0, 3).trim();
                if ("ER".equals(fieldName)) {
                    continue;
                }
                Field declaredField = Article.class.getDeclaredField(fieldName);
                String value = s.substring(3).trim();
                if (declaredField.getType() == String.class) {
                    invokeValue(a, fieldName, value);
                } else {
                    invokeArrayValue(a, fieldName, value);
                }
//                subjects.add(fieldName);
//                String c = s.substring(3, s.length() - 1);
                //此时就读到了一行字段的数据
//                System.out.print(t + "->" + c);
                stringBuilder.delete(0, stringBuilder.length());
            }
        }
        return a;
    }

    private static void invokeArrayValue(Article a, String fieldName, String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = Article.class.getDeclaredMethod("set" + fieldName, String[].class);
        String[] res = null;
        //分号空格分割
        if (setSplitWithSemicolon.contains(fieldName)) {
            res = value.split(";");

        } else {
            res = value.split("\n");
        }
        String[] result = new String[res.length];
        int i = 0;
        for (String s : res) {
            result[i++] = s.trim();
        }
        res = result;
        method.invoke(a, (Object) res);
    }

    private static void invokeValue(Article a, String fieldName, String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = Article.class.getDeclaredMethod("set" + fieldName, String.class);
        value = value.trim();
        method.invoke(a, value);
    }

    private static boolean isAnArticle(String str) {
//        String substring = str.substring(str.length() - 10);
        int offset = 1;
        int countN = 0;
        while ((offset < str.length() && str.charAt(str.length() - offset) == '\r') || (offset < str.length() && str.charAt(str.length() - offset) == '\n')) {
            if (str.charAt(str.length() - offset) == '\n') {
                countN++;
            }
            offset++;
        }
        return countN >= 2;
//                && str.charAt(str.length() - 3) == 0x0A
//                && str.charAt(str.length() - 3 - 1) == 0x0D;
//        return b;


    }

    public static void write(Object map, String s) throws IOException {
        check(s);
        try (DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(s)))) {
            JSONObject.writeJSONString(dataOutputStream, StandardCharsets.UTF_8, map, SerializerFeature.DisableCircularReferenceDetect);
        }
    }

    //约定必须实现 Entry<K,V>接口
    public <E extends Map.Entry<String, Integer>> void quickSort(List<E> list, int lo, int hi) {
        if (lo >= hi) {
            return;
        }
        int par = partion(list, lo, hi);
        quickSort(list, lo, par - 1);
        quickSort(list, par + 1, hi);
    }

    private <E extends Map.Entry<String, Integer>> int partion(List<E> list, int lo, int hi) {
        int i = lo, j = hi + 1;
        Map.Entry<String, Integer> num = list.get(lo);

        while (true) {
            while (i != hi && list.get(++i).getValue() > num.getValue()) {
            }
            while (j != lo && list.get(--j).getValue() < num.getValue()) {
            }
            if (i >= j) {
                break;
            }
            swap(list, j, i);
        }
        swap(list, lo, j);
        return j;
    }

    private <E extends Map.Entry<String, Integer>> void swap(List<E> list, int i, int j) {
        E entry = list.get(i);
        list.set(i, list.get(j));
        list.set(j, entry);
    }
}
